// -----------------------------------------------------------------------------
//
//  Author : 	Duke Zhou
//  Data : 		2016/7/19
//
// -----------------------------------------------------------------------------
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using System.IO;

namespace Jelly.Editor.AssetBundle
{
    [System.Serializable]
    /// <summary>
    /// 每个ResNode对应一个Asset资源，如果isRoot == true，则是根节点，即会被直接load的节点；有的ResNode是group根节点，则group里保存的这一团节点会被同时依赖
    /// </summary>
    internal class ResNode2
    {
        public string path;
    
        private string assetName;
        private string assetExtension;
    
        public bool compressed;     // 是否合并到一组里
    
        public bool isRoot;
    
        /// <summary>
        /// 合并进入这个节点的其他节点
        /// 如果是group的根节点，通过递归找所有group的group能找到一个bundle里的所有资源
        /// </summary>
        public List<ResNode2> group;

        /// <summary>
        /// 原始的全依赖
        /// </summary>
        public List<ResNode2> originDependence;
    
        /// <summary>
        /// 压缩后的依赖
        /// </summary>
        public List<ResNode2> dependence;
    
        /// <summary>
        /// 哪些节点依赖这个节点
        /// </summary>
        public List<ResNode2> whoDependOnMe;

        /// <summary>
        /// 原本的直接依赖，用于检测标记一个被依赖的资源是否要显示指定进入某个bundle
        /// 如果不显示指定，可能因为被多个bundle依赖而冗余，在animator和overrideController中冗余会导致override missing
        /// 如果只被一个bundle依赖仍然指定，这个bundle中有不同path但同文件名的资源会构建时报hash冲突
        /// </summary>
        public HashSet<string> originDirectDep;

        public HashSet<ResNode2> whoOriginDirectDependOnMe;

        public ResNode2(string path)
        {
            this.path = path;
            assetName = Path.GetFileName(path);
            assetExtension = Path.GetExtension(path);
            compressed = false;
            isRoot = false;
            var directDeps = AssetDatabase.GetDependencies(this.path, false);
            originDirectDep = new HashSet<string>(directDeps);
            whoOriginDirectDependOnMe = new HashSet<ResNode2>();
        }
    
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(200);
            sb.AppendFormat("subgraph \"{0}.sub\" {{", assetName);
            //        sb.AppendLine("node [fontsize=20, shape = box];");
    
            sb.AppendFormat("        \"{0}\" [ color=\"{1}\" label=\"{2}\"];\n", assetName, GetColorByExtersion(assetExtension), GetGroupString(this));
            int count2 = dependence != null ? dependence.Count : 0;
            for (int i = 0; i < count2; ++i)
            {
                ResNode2 dependenceNode = dependence[i];
                if (dependenceNode != null)
                    sb.AppendFormat("\"{0}\" -> \"{1}\";\n", assetName, dependenceNode.assetName);
                //            sb.AppendLine(dependence.ToString());
            }
            //        int count2 = whoDependOnMe != null ? whoDependOnMe.Count : 0;
            //        for(int i = 0; i < count2; ++i)
            //        {
            //            ResNode2 dependenceNode = whoDependOnMe[i];
            //            if(dependenceNode != null)
            //                sb.AppendFormat("\"{0}\" -> \"{1}\";\n", dependenceNode.assetName, assetName);
            //            //            sb.AppendLine(dependence.ToString());
            //        }
            sb.AppendLine("}");
            return sb.ToString();
        }
    
        /// <summary>
        /// 递归地拿到所有Group节点的Path
        /// </summary>
        /// <returns>The all group path.</returns>
        public static void GetAllGroupPath(ResNode2 node, HashSet<string> pathList)
        {
            pathList.Add(node.path);

            int count = node.group != null ? node.group.Count : 0;
            if (count == 0)
            {
                return;
            }
            for (int i = 0; i < count; ++i)
            {
                ResNode2 groupSibling = node.group [i];
                GetAllGroupPath(groupSibling, pathList);
            }
        }
        
        
        /// <summary>
        /// 一个资源只被这个bundle依赖不需要显式设置到bundle.assetNames
        /// </summary>
        /// <param name="node"></param>
        /// <param name="assetNameList"></param>
        /// <param name="reverseGroupMap"></param>
        /// <param name="bundlePath"></param>
        public static void GetBundleNecessaryAssetNameList(ResNode2 node, HashSet<string> assetNameList, 
            Dictionary<string, string> reverseGroupMap, string bundlePath, string loadablePrefix)
        {
            bool addToBundleAssetNames = node.path == bundlePath || node.path.StartsWith(loadablePrefix);
            if (addToBundleAssetNames == false)
            {
                foreach (var directDepOnMe in node.whoOriginDirectDependOnMe)
                {
                    if (reverseGroupMap.TryGetValue(directDepOnMe.path, out var someBundlePath))
                    {
                        // 还被其他bundle依赖，需要显式设置
                        if (someBundlePath != bundlePath)
                        {
                            addToBundleAssetNames = true;
                            break;
                        }
                    }
                    else
                    {
                        Debug.Log($"reverseGroup not found:{directDepOnMe.path}");
                    }

                }
            }

            if (addToBundleAssetNames)
            {
                assetNameList.Add(node.path);
            }

            int count = node.group != null ? node.group.Count : 0;
            if (count == 0)
            {
                return;
            }
            for (int i = 0; i < count; ++i)
            {
                ResNode2 groupSibling = node.group[i];
                GetBundleNecessaryAssetNameList(groupSibling, assetNameList, reverseGroupMap, bundlePath, loadablePrefix);
            }
        }
        

    
//        void GetGroupPath(ResNode2 node, List<string> pathList)
//        {
//            int count = node.group != null ? node.group.Count : 0;
//            if (count == 0)
//            {
//                if (!pathList.Contains(node.path))
//                {
//                    pathList.Add(node.path);
//                }
//                return;
//            }
//            for (int i = 0; i < count; ++i)
//            {
//                ResNode2 groupSibling = node.group [i];
//                GetGroupPath(groupSibling, pathList);
//            }
//        }
    
        static string GetGroupString(ResNode2 node)
        {
            int count = node.group != null ? node.group.Count : 0;
            if (count == 0)
            {
                return node.assetName;
            }
            StringBuilder sb = new StringBuilder(200);
            sb.AppendLine(node.assetName);
            for (int i = 0; i < count; ++i)
            {
                ResNode2 groupSibling = node.group [i];
                string siblingString = GetGroupString(groupSibling);
                if (!string.IsNullOrEmpty(siblingString))
                {
                    sb.AppendLine(" + " + siblingString);
                }
            }
            return sb.ToString();
        }
        
        string GetColorByExtersion(string extension)
        {
            switch (extension)
            {
                case ".prefab":
                    return "red";
                case ".mat":
                    return "green";
                case ".shader":
                    return "blue";
                case ".tex":
                    return "yellow";
                case ".txt":
                    return "black";
                case ".asset":
                    return "grey";
            }
            return "black";
        }
    }
}
